package com.atguigu.gmall.order.controller;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.client.CartFeignClient;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.util.AuthContextHolder;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.model.user.UserAddress;
import com.atguigu.gmall.order.service.OrderService;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.atguigu.gmall.user.client.UserFeignClient;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

@RestController
@RequestMapping("api/order")
public class OrderApiController {

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private CartFeignClient cartFeignClient;

    @Autowired
    private OrderService orderService;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;


    // 网关拦截验证：if (antPathMatcher.match("/api/**/auth/**", path)) {...}

    /**
     * 确认订单
     * 1 查看订单 --->>> 必须登录 --->>> 符合网关拦截认证要求即需要登录 故映射地址为： @GetMapping("auth/trade")
     * 2 trade.html页面需要：userAddressList、${detailArrayList}、totalAmount、totalNum 等数据渲染页面
     * 3 仿照 ItemServiceImpl 使用 Map 封装数据返回页面
     *
     * @param request
     * @return
     */
    @GetMapping("auth/trade")
    public Result<Map<String, Object>> trade(HttpServletRequest request) {
        /*
        思路：
            1 通过 userFeignClient 获取用户收货地址列表
            2 通过 cartFeignClient 获取购买清单列表
            3 计算总金额
            4 计算商品总件数
         */

        // 获取 userId
        String userId = AuthContextHolder.getUserId(request);

        // 远程调用 - 获取用户收货地址列表
        List<UserAddress> userAddressList = userFeignClient.findUserAddressListByUserId(userId);

        // 远程调用 - 获取购买清单列表
        List<CartInfo> cartInfoCheckedList = cartFeignClient.getCartCheckedList(userId);

        // 声明一个集合保存订单明细
        List<OrderDetail> orderDetailList = new ArrayList<>();
        // 声明变量存储 商品总件数
        int totalNum = 0;

        // 判断 购买清单列表 不为空
        if (!CollectionUtils.isEmpty(cartInfoCheckedList)) {
            // 循环获取 cartInfo
            for (CartInfo cartInfo : cartInfoCheckedList) {
                // 创建一个 订单明细对象
                OrderDetail orderDetail = new OrderDetail();

                orderDetail.setSkuId(cartInfo.getSkuId());
                orderDetail.setSkuName(cartInfo.getSkuName());
                orderDetail.setSkuNum(cartInfo.getSkuNum());
                orderDetail.setImgUrl(cartInfo.getImgUrl());
                orderDetail.setOrderPrice(cartInfo.getSkuPrice());

                // 方式二：计算商品总件数
                totalNum += cartInfo.getSkuNum();

                // 将 订单明细对象添加到集合
                orderDetailList.add(orderDetail);
            }
        }

        // 计算总金额 OrderInfo.sumTotalAmount()
        // 计算总结金额必须先为 orderDetailList 赋值 才能调用 sumTotalAmount() 方法计算
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderDetailList(orderDetailList);
        // sumTotalAmount()计算完后将结果赋值给 OrderInfo.totalAmount
        orderInfo.sumTotalAmount();

        // 方式一：计算商品总件数
        /*
         方式一：计算有几个 spu 的 sku ===>>> orderDetailList.size()
         方式二：计算有几个 spu 中的 skuId 的个数 ===>>> 遍历集合时就为totalNum赋值 totalNum += cartInfo.getSkuNum()
         */
        // int totalNum = orderDetailList.size();

        // 获取订单的流水号[同时会将流水号保存进redis]
        String tradeNo = orderService.getTradeNo(userId);

        // 声明一个 Map 保存数据
        Map<String, Object> map = new HashMap<>();

        // 保存订单流水号 {页面需要在提交订单时传递流水号 tradeNo: [[${tradeNo}]]}
        map.put("tradeNo", tradeNo);

        // 保存数据
        map.put("userAddressList", userAddressList);
        map.put("detailArrayList", orderDetailList);
        map.put("totalNum", totalNum);
        map.put("totalAmount", orderInfo.getTotalAmount());

        // 返回数据
        return Result.ok(map);
    }

    /**
     * 提交订单
     * http://api.gmall.com/api/order/auth/submitOrder?tradeNo=null
     *
     * @param orderInfo
     * @param request
     * @return
     */
    @PostMapping("auth/submitOrder")
    public Result submitOrder(@RequestBody OrderInfo orderInfo, HttpServletRequest request) {

        // 获取到用户Id 并 设置用户Id
        String userId = AuthContextHolder.getUserId(request);

        orderInfo.setUserId(Long.parseLong(userId));

        // 页面提交订单时的url: this.api_name + '/auth/submitOrder?tradeNo=' + tradeNo
        // 获取页面传递的流水号
        String tradeCodeNo = request.getParameter("tradeNo");
        // 调用服务层进行比较
        boolean flag = orderService.checkTradeCode(userId, tradeCodeNo);
        // 判断比较结果
        if (!flag) {// 页面传递的流水号和缓存中的不一致
            return Result.fail().message("请刷新页面后再次提交订单！");
        }

        // 删除缓存中的流水号
        orderService.deleteTradeNo(userId);

        // 声明一个集合存储验证不通过的信息
        List<String> errorList = new ArrayList<>();

        // 声明一个 CompletableFuture 集合
        List<CompletableFuture> completableFutureList = new ArrayList<>();

        // 验证库存：验证每个商品
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();

        if (!CollectionUtils.isEmpty(orderDetailList)) {
            // 循环遍历验证每个商品
            for (OrderDetail orderDetail : orderDetailList) {

                // 验证库存
                /*boolean result = orderService.checkStock(orderDetail.getSkuId(), orderDetail.getSkuNum());
                if (!result) {// 表示验证没通过
                    return Result.fail().message(orderDetail.getSkuName() + "库存不足！");
                }*/
                CompletableFuture<Void> checkStockCompletableFuture = CompletableFuture.runAsync(() -> {
                    boolean result = orderService.checkStock(orderDetail.getSkuId(), orderDetail.getSkuNum());

                    if (!result) {// 表示验证没通过

                        errorList.add(orderDetail.getSkuName() + "库存不足！");
                    }
                }, threadPoolExecutor);

                // 添加验证库存的异步编排进 completableFutureList
                completableFutureList.add(checkStockCompletableFuture);

                // 验证价格
                /*// 获取商品最新价格
                BigDecimal skuPrice = productFeignClient.getSkuPrice(orderDetail.getSkuId());
                // 订单中的价格与商品的最新价格进行比较
                if (orderDetail.getOrderPrice().compareTo(skuPrice) != 0) {// 价格不相同
                    // 如果价格有变动 需要获取最新价格将购物车中的数据变为最新的
                    // 查询数据库最新价格 ===>>> 放入缓存 ===>>> 更新购物车价格 ===>>> 更新订单价格

                    // 重新查询价格
                    cartFeignClient.loadCartCache(userId);

                    return Result.fail().message(orderDetail.getSkuName() + "价格有变动！");
                }*/
                CompletableFuture<Void> checkPriceCompletableFuture = CompletableFuture.runAsync(() -> {
                    // 获取商品最新价格
                    BigDecimal skuPrice = productFeignClient.getSkuPrice(orderDetail.getSkuId());

                    // 订单中的价格与商品的最新价格进行比较
                    if (orderDetail.getOrderPrice().compareTo(skuPrice) != 0) {// 价格不相同
                        // 如果价格有变动 需要获取最新价格将购物车中的数据变为最新的
                        // 查询数据库最新价格 ===>>> 放入缓存 ===>>> 更新购物车价格 ===>>> 更新订单价格

                        // 重新查询价格
                        cartFeignClient.loadCartCache(userId);
                        //Result.fail().message(orderDetail.getSkuName() + "价格有变动！");
                        errorList.add(orderDetail.getSkuName() + "价格有变动！");
                    }
                }, threadPoolExecutor);

                // 添加验证价格的异步编排进 completableFutureList
                completableFutureList.add(checkPriceCompletableFuture);
            }

            // 合并异步编排
            // allOf(CompletableFuture<?>... cfs) 的参数是可变形参 故我们需要把 completableFutureList 转变为数组
            // 将异步编排集合变为数组 completableFutureList.toArray(需要数组的参数、数组的长度为 completableFutureList 的 大小)
            CompletableFuture
                    .allOf(completableFutureList.toArray(new CompletableFuture[completableFutureList.size()]))
                    .join();

            // 判断 存储验证不通过的信息的 集合 该集合存储的是字符串
            if (errorList.size() > 0) { // 如果集合大小大于0 说明有验证不通过的商品

                // 使用 lang3.StringUtils 工具类将集合里的字符串取出来 返回错误提示到页面

                // join() 第一个参数表示需要分割的源数据 第二个参数表示用什么来分割
                return Result.fail().message(StringUtils.join(errorList, ","));
            }
        }

        // 调用服务层
        Long orderId = orderService.saveOrderInfo(orderInfo);

        // 返回订单Id
        return Result.ok(orderId);
    }

    /**
     * 根据订单号获取订单信息
     *
     * @param orderId
     * @return
     */
    @GetMapping("inner/getOrderInfo/{orderId}")
    public OrderInfo getOrderInfo(@PathVariable(value = "orderId") Long orderId) {
        return orderService.getOrderInfo(orderId);
    }

    /**
     * 拆单
     * 拆单接口：http://localhost:8204/api/order/orderSplit?orderId=xxx&wareSkuMap=xxx
     *
     * @param request
     * @return
     */
    @RequestMapping("orderSplit")
    public String orderSplit(HttpServletRequest request) {
        // 获取拆单需要的参数
        String orderId = request.getParameter("orderId");
        // [{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}]
        String wareSkuMap = request.getParameter("wareSkuMap");

        // 调用服务层进行拆单 返回子订单集合
        List<OrderInfo> subOrderInfoList = orderService.orderSplit(Long.parseLong(orderId), wareSkuMap);

        // 创建一个 List 保存 Map
        List<Map> subOrderInfoMapList = new ArrayList<>();

        // 循环遍历获取子订单对象
        for (OrderInfo orderInfo : subOrderInfoList) {

            // 将 子订单对象 转换为 map
            Map map = orderService.initWareOrder(orderInfo);

            // 将 map 添加进 orderInfoMapList
            subOrderInfoMapList.add(map);
        }
        return JSON.toJSONString(subOrderInfoMapList);
    }

    /**
     * 秒杀提交订单
     *
     * @param orderInfo
     * @return
     */
    @PostMapping("inner/seckill/submitOrder")
    public Long submitOrder(@RequestBody OrderInfo orderInfo) {
        Long orderId = orderService.saveOrderInfo(orderInfo);
        return orderId;
    }
}
